package main

import (
	"fmt"
	"strconv"
	"sync"
	"time"
)

// 同步计数器 只定义无需赋值
var wg sync.WaitGroup

// 全局变量
var totalNum int

// 互斥锁一个协程在执行逻辑的时候另外的协程不执行
var lock sync.Mutex

// RWMutex是一个读写锁，其经常用于读次数远远多于写次数的场景 在读的时候，数据之间不产生影响，   写和读之间才会产生影响
var rwMutex sync.RWMutex

func main() {
	// 如果主线程退出了，则协程即使还没有执行完毕，也会退出
	//协程也可以在主线程没有退出前，就自己结束了，比如完成了自己的任务
	fmt.Println("协程=======================================================================重要课程")
	fmt.Println()
	// 启动协程 启动协程关键字 go
	go test()

	for i := 0; i < 2; i++ {
		fmt.Printf("hello msb+ %v\n", strconv.Itoa(i))
		// 阻塞一秒
		time.Sleep(time.Second)
	}
	fmt.Println()

	fmt.Println("协程闭包=======================================================================重要")
	fmt.Println()
	//匿名函数+外部变量 = 闭包
	for i := 1; i <= 5; i++ {
		//启动一个协程 启动协程关键字 go
		//使用匿名函数，直接调用匿名函数
		go func(n int) {
			fmt.Println(n)
		}(i) // i相当于传参传给n
	}
	//主线程等待
	time.Sleep(time.Second * 2)
	fmt.Println()

	fmt.Println("协程sync同步单元=======================================================================重要")
	fmt.Println()

	// 启动五个协程
	for i := 1; i <= 5; i++ {
		//协程开始的时候加1操作 Add中加入的数字和协程的次数一定要保持一致
		wg.Add(1)
		go func(n int) {
			// 计数器减1结合defer关键字使用，defer关键字使用将这段代码压入栈，等都执行玩在执行这段代码
			defer wg.Done()
			fmt.Println(n)
			//wg.Done()  //协程执行完成减1
		}(i)
	}
	//主线程一直在阻塞，什么时候wg减为0了，就停止
	wg.Wait()
	fmt.Println()

	fmt.Println("协程sync.Mutex互斥锁同步单元=======================================================================重要")
	fmt.Println()
	// 代表有两个协程
	wg.Add(2)
	//启动协程 启动协程关键字 go
	go add()
	go sub()
	wg.Wait()
	fmt.Println(totalNum)

	fmt.Println()

	fmt.Println("协程sync.RWMutex读写锁同步单元=======================================================================重要")
	fmt.Println()
	wg.Add(6)
	//启动协程 ---> 场合：读多写少
	for i := 0; i < 5; i++ {
		go read()
	}
	go write()
	wg.Wait()
}

// 定义函数
func test() {
	for i := 0; i < 2; i++ {
		fmt.Printf("hello goLang+ %v\n", strconv.Itoa(i))
		// 阻塞一秒
		time.Sleep(time.Second)
	}
}

func add() {
	defer wg.Done()
	for i := 0; i < 100; i++ {
		//加锁
		lock.Lock()
		totalNum = totalNum + 1
		//解锁
		lock.Unlock()
	}
}
func sub() {
	defer wg.Done()
	for i := 0; i < 100; i++ {
		//加锁
		lock.Lock()
		totalNum = totalNum - 1
		//解锁：
		lock.Unlock()
	}
}

func read() {
	defer wg.Done()
	//如果只是读数据，那么这个锁不产生影响，但是读写同时发生的时候，就会有影响
	rwMutex.RLock()
	fmt.Println("开始读取数据")
	time.Sleep(time.Second)
	fmt.Println("读取数据成功")
	rwMutex.RUnlock()

}

func write() {
	defer wg.Done()
	rwMutex.Lock()
	fmt.Println("开始修改数据")
	time.Sleep(time.Second * 2)
	fmt.Println("修改数据成功")
	rwMutex.Unlock()
}
